#pragma once

#ifdef REIGN_DESKTOP // Only for desktop applications
#include "SehnsuchtPrecompiled.hxx"

namespace Sehnsucht
{
	template <typename TElement>
	class Array 
	{
		typedef TElement *Collection;
		typedef TElement *const TElementPtrConst;

	protected:
		Collection m_Elements; // Collection elements
		UInt32 m_NumElements; // Collection size 

		HResult Allocate()
		{
			//m_Elements = (Collection)(malloc(m_NumElements)); // Try allocate memory to store array elements
			m_Elements = alloc_new TElement[m_NumElements];
			if (!m_Elements) return E_OUTOFMEMORY; // Failed to allocate memory
			else return( S_OK ); // Memory was allocated. 
		}

		Void Deallocate()
		{
			if (m_Elements) // If the memory was previously allocated 
			{
				//free((ObjectPtr)(m_Elements)); // Free all the dynamically-allocated memory 
				Safe_DeleteArray(m_Elements); 
				m_Elements = nullptr; // And avoid memory access violations 
			}
		}


	public:

		// Gets or sets the array size. 
		DeclGetSetPropOnly( UInt32, Length );
		DeclConstTypedGetterFor( UInt32, Length )
		{
			return( m_NumElements );
		}
		DeclTypedSetterFor( UInt32, Length )
		{
			Deallocate();
			m_NumElements = Value;
			Allocate();
		}

		// Gets the array pointer. 
		DeclGetPropOnly( TElementPtrConst, Data );
		DeclConstTypedGetterFor( TElementPtrConst, Data )
		{
			return( m_Elements );
		}

	public:
		Array( ) : m_Elements(nullptr), m_NumElements(0)
		{
		}

		Array( _In_ UInt32 NumElements, _Outptr_opt_result_maybenull_ HResult *Result = nullptr ) : 
			m_Elements(nullptr), m_NumElements(NumElements)
		{
			if (Result) (*Result) = Allocate();
			else Allocate();
		}

		~Array()
		{
			Deallocate();
		}

		// Gets element by reference. 
		// Errors: 
		// - E_BOUNDS - Index out of range
		// - E_FAIL - Memory not allocated
		_Out_ HResult Get( _In_ UInt32 uiIndex, _In_ TElement &Ref) const
		{
			if ( uiIndex > m_NumElements ) // Index out of range
			{
				return( E_BOUNDS ); 
			}
			else if ( !m_Elements ) // Elements not allocated
			{
				return( E_FAIL );
			}
			else // Everything is ok
			{
				Ref = m_Elements[uiIndex];
				return( S_OK ); 
			}
		}
			
		// Gets element by reference. 
		// Errors: 
		// - E_BOUNDS - Index out of range
		// - E_FAIL - Memory not allocated
		_Out_ HResult Set( _In_ UInt32 uiIndex, _In_ TElement const &Ref)
		{
			if ( uiIndex > m_NumElements ) // Index out of range
			{
				return( E_BOUNDS ); 
			}
			else if ( !m_Elements ) // Elements not allocated
			{
				return( E_FAIL );
			}
			else // Everything is ok
			{
				alloc_new (&m_Elements[uiIndex]) TElement(Ref);
				m_Elements[uiIndex] = Ref;
				return( S_OK ); 
			}
		}
			
		// Gets element ref. 
		TElement &operator []( _In_ UInt32ConstRef uiIndex )
		{
			return( m_Elements[uiIndex] );
		}

		// Gets element uneditable ref. 
		TElement const &operator []( _In_ UInt32ConstRef uiIndex ) const
		{
			return( m_Elements[uiIndex] );
		}

	private:

		// Prevent copying.
		Array( Array const& );
		Array &operator=( Array const& );
	};
};

#endif